﻿using FriendAnalyzer.Infrastructure.MachineLearning.Model;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FriendAnalyzer.Infrastructure.MachineLearning.Analysis
{
    public class FacebookUserClassifier : FuzzyCMeans<UserFeatures>
    {
        public FacebookUserClassifier(UserFeatures[] database, uint leaderCount) : base(database, leaderCount)
        {
            DistanceFunc = (x, y) =>
            {
                var dist = .0;
                for (int i = 0; i < x.Features.Length; i++)
                {
                    dist += Math.Pow(x.Features[i] - y.Features[i], 2);
                }

                return dist != 0 ? dist : 0.000005;
            };

            CentroidFunc = (db, w, fuzziness) =>
            {
                double denominator = w.Select(x => Math.Pow(x, fuzziness)).Sum();
                var centroidFeatures = new double[db[0].Features.Length];

                if (denominator != 0)
                {
                    for (int i = 0; i < db.Length; i++)
                    {
                        double wij = Math.Pow(w[i], fuzziness);
                        for (int j = 0; j < centroidFeatures.Length; j++)
                        {
                            centroidFeatures[j] += wij * db[i].Features[j];
                        }
                    }

                    for (int i = 0; i < centroidFeatures.Length; i++)
                    {
                        centroidFeatures[i] /= denominator;
                    }
                }

                return new UserFeatures { UserId = 0, Features = centroidFeatures };
            };
        }

        private UserFeatures GetLeaderUserClosestTo(UserFeatures centroid)
        {
            var leaderIndex = 0;
            var minDistance = double.MaxValue;

            for (int i = 0; i < Database.Length; i++)
            {
                var dist = DistanceFunc(Database[i], centroid);
                if (dist < minDistance)
                {
                    minDistance = dist;
                    leaderIndex = i;
                }
            }

            return Database[leaderIndex];
        }

        public Dictionary<UserFeatures, UserFeatures[]> GetGroupedUsers()
        {
            var groupedUsers = new Dictionary<UserFeatures, UserFeatures[]>();

            for (int i = 0; i < K; i++)
            {
                var leader = GetLeaderUserClosestTo(Centroids[i]);
                if (!groupedUsers.ContainsKey(leader))
                {
                    groupedUsers.Add(GetLeaderUserClosestTo(Centroids[i]), GetClusterMembers(i));
                }
            }

            return groupedUsers;
        }
    }
}
